Windows 这个多作业系统除了协调
应用程序的执行、分配内存、
管理资源之外, 它同时也是一个很大的服务中心,调用这个服务中心的各种服务(每一种服务就是一个函数),可以帮应用程序达到开启视窗、描绘图形、使用
周边设备等目的,由于这些函数服务的对象是应用程序(Application), 所以便称之为 Application Programming Interface,简称
API 函数。WIN32 API也就是
Microsoft Windows 32位平台的
应用程序编程接口。
简介
基本信息
其中32位Windows操作系统的
编程接口称为
Win32 API,以便与以前16位版本Windows编程接口(16位Windows API)区别开来。
函数分类
Windows API包括几千个可调用的函数,它们大致可以分为以下几个大类:
基本服务;
用户界面服务;
图形多媒体服务;
消息和协作;
网络;
发展现状
当
Windows操作系统开始占据
主导地位的时候,开发Windows平台下的
应用程序成为人们的需要。而在Windows程序设计领域处于发展的初期,Windows
程序员所能使用的编程工具唯有
API函数,这些函数是Windows提供给应用程序与操作系统的接口,他们犹如“积木块”一样,可以搭建出各种界面丰富,功能灵活的应用程序。所以可以认为API函数是构筑整个Windows框架的基石,在它的下面是Windows的操作系统核心,而它的上面则是所有的华丽的Windows应用程序。
程序员想编写具有Windows风格的软件,必须借助
API,API也因此被赋予至高无上的地位。但是,如若没有合适的Windows编程平台,那么Windows开发是一项很复杂的工作。在可视化编程
IDE出来之前,那时的
Windows程序开发还是比较复杂的工作,程序员必须熟记一大堆常用的API函数,而且还得对Windows操作系统有深入的了解。然而随着软件技术的不断发展,在Windows平台上出现了很多优秀的
可视化编程环境,程序员可以采用“所见即所得”的编程方式来开发具有精美
用户界面和功能强大的应用程序。
这些优秀可视化编程环境操作简单、界面友好(诸如
VB、VC++、DELPHI等),在这些工具中提供了大量的
类库和各种控件,它们替代了API的神秘功能,事实上这些类库和控件都是构架在WIN32 API函数基础之上的,是封装了的API函数的集合。它们把常用的API函数的组合在一起成为一个控件或类库,并赋予其方便的使用方法,所以极大的加速了Windows
应用程序开发的过程。有了这些控件和类库,程序员便可以把主要精力放在程序整体功能的设计上,而不必过于关注
技术细节。
实际上如果我们要开发出更灵活、更实用、更具效率的应用程序,必然要涉及到直接使用API函数,虽然类库和控件使应用程序的开发简单的多,但它们只提供Windows的一般功能,对于比较复杂和特殊的功能来说,使用类库和控件是非常难以实现的,这时就需要采用API函数来实现。
这也是API函数使用的场合,所以我们对待API函数不必刻意去研究每一个函数的用法,那也是不现实的(能用得到的API函数有几千个呢)。正如某位大虾所说:API不要去学,在需要的时候去查API帮助就足够了。但是,许多API函数令人难以理解,易于误用,还会导致出错,这一切都阻碍了它的推广。
概要介绍
应用程序接口为:“‘计算机
操作系统(Operating system)’或‘
程序库’提供给应用程序调用使用的代码”。其主要目的是让
应用程序开发人员得以调用一组
例程功能,而无须考虑其底层的
源代码为何、或理解其内部
工作机制的细节。API本身是抽象的,它仅定义了一个接口,而不涉入应用程序如何实现的细节。
例如,
图形库中的一组API定义了绘制指针的方式,可于屏幕上显示指针。当应用程序需要指针功能时,可引用、
编译时链接到这组API,而
运行时就会调用此API的实现(库)来显示指针。
应用程序接口是一组数量上千、极其复杂的函数和副程序,可让
程序设计师做很多任务作,譬如“读取文件”、“显示菜单”、“在视窗中显示网页”等等。操作系统的API可用来分配
存储器或读取文件。许多系统应用程序借由API接口来实现,像是
图形系统、数据库、网络
Web服务,甚至是在线游戏。
应用程序接口有诸多不同设计。用于快速执行的接口通常包括函数、常量、变量与
数据结构。也有其它方式,如通过解释器,或是提供抽象层以遮蔽同API实现相关的信息,确保使用API的代码无需更改而适应实现变化。
应用程序接口经常是
软件开发工具包(SDK)的一部分。
表达方式
C #
FunctionName(Type parameter1,Type parameter2...);
API举例
C#中调用Windows API:
一、调用格式
using System.Runtime.InteropServices; //引用此
名称空间,简化后面的
代码...
//使用
DllImportAttribute特性来引入api函数,注意声明的是空方法,即方法体为空。
public static extern ReturnType FunctionName(type
arg1,type arg2,...);
//调用时与调用其他方法并无区别
可以使用字段进一步说明特性,用逗号隔开,如:
DllImportAttribute特性的公共字段如下:
1、CallingConvention 指示向非托管实现传递方法参数时所用的 CallingConvention
值。
CallingConvention.Cdecl :
调用方清理
堆栈。它使您能够调用具有 varargs 的函数。
CallingConvention.StdCall :
被调用方清理堆栈。它是从
托管代码调用非托管函数的默认约定。
2、CharSet 控制调用函数的名称版本及指示如何向方法封送 String 参数。
此字段被设置为 CharSet 值之一。如果 CharSet 字段设置为
Unicode,则所有字符串参数在传递到非托管实现之前都转换成 Unicode 字符。这还导致向 DLL
EntryPoint 的名称中追加字母“W”。如果此字段设置为 Ansi,则
字符串将转换成
ANSI字符串,同时向 DLL EntryPoint 的名称中追加字母“A”。大多数 Win32 API
使用这种追加“W”或“A”的约定。如果 CharSet 设置为 Auto,则这种转换就是与平台有关的(在
Windows NT 上为 Unicode,在 Windows 98 上为 Ansi)。CharSet
的
默认值为 Ansi。CharSet 字段也用于确定将从指定的 DLL
导入哪个版本的函数。CharSet.Ansi 和 CharSet.Unicode 的名称匹配规则大不相同。对于
Ansi 来说,如果将 EntryPoint
设置为“MyMethod”且它存在的话,则返回“MyMethod”。如果 DLL
中没有“MyMethod”,但存在“MyMethodA”,则返回“MyMethodA”。对于 Unicode
来说则正好相反。如果将 EntryPoint
设置为“MyMethod”且它存在的话,则返回“MyMethodW”。如果 DLL
中不存在“MyMethodW”,但存在“MyMethod”,则返回“MyMethod”。如果使用的是
Auto,则匹配规则与平台有关(在 Windows NT 上为 Unicode,在 Windows 98 上为
Ansi)。如果 ExactSpelling 设置为 true,则只有当 DLL
中存在“MyMethod”时才返回“MyMethod”。
3、EntryPoint 指示要调用的 DLL 入口点的名称或序号。
如果你的方法名不想与api函数同名的话,一定要指定此参数,例如:
public static extern int MsgBox(IntPtr hWnd,
stringtxt,string caption,int type);
4、ExactSpelling 指示是否应修改非托管 DLL 中的入口点的名称,以与 CharSet
字段中指定的 CharSet 值相对应。如果为 true,则当
DllImportAttribute.CharSet 字段设置为 CharSet 的 Ansi
值时,向方法名称中追加字母 A,当 DllImportAttribute.CharSet 字段设置为
CharSet 的 Unicode 值时,向方法的名称中追加字母 W。此字段的默认值是 false。
5、PreserveSig 指示托管方法签名不应转换成返回
HRESULT、并且可能有一个对应于
返回值的附加
[out,retval] 参数的非托管签名。
6、SetLastError 指示被调用
方在从属性化方法返回之前将调用 Win32 API
SetLastError。true 指示调用方将调用 SetLastError,默认为
false。运行时封送拆收器将调用
GetLastError 并缓存返回的值,以防其被其他 API
调用重写。用户可通过调用 GetLastWin32Error 来检索
错误代码。
二、参数类型:
1、数值型直接用对应的就可。(DWORD -> int,WORD -> Int16)
2、API中字符串指针类型 -> .net中string
3、API中句柄 (dWord) -> .net中IntPtr
4、API中结构 -> .net中结构或者类。注意这种情况下,要先用StructLayout特性限定声明结构或类
公共语言运行库利用StructLayoutAttribute
控制类或结构的
数据字段在托管内存中的物理布局,即类或结构需要按某种方式排列。如果要将类传递给需要指定布局的
非托管代码,则显式控制类布局是重要的。它的
构造函数中用LayoutKind值初始化
StructLayoutAttribute 类的新实例。LayoutKind.Sequential
用于强制将成员按其出现的顺序进行顺序布局。
LayoutKind.Explicit 用于控制每个
数据成员的精确位置。利用 Explicit,
每个成员必须使用 FieldOffsetAttribute 指示此字段在类型中的位置。如:
[StructLayout(LayoutKind.Explicit,Size=16,
CharSet=CharSet.Ansi)]
public class MySystemTime
{
[Field
Offset(0)]public ushort wYear;
[FieldOffset⑵]public ushort wMonth;
[FieldOffset⑷]public ushort wDayOfWeek;
[FieldOffset⑹]public ushort wDay;
[FieldOffset⑻]public ushort wHour;
[FieldOffset⑽]public ushort wMinute;
[FieldOffset⑿]public ushort wSecond;
[FieldOffset⒁]public ushort wMilliseconds;
}
下面是针对API中
OSVERSIONINFO结构,在.net中定义对应类或结构的例子:
/**********************************************
* API中定义原结构声明
* OSVERSIONINFOA STRUCT
* dwOSVersionInfoSize DWORD
* dwMajorVersion DWORD
* dwMinorVersion DWORD
* dwBuildNumber DWORD
* dwPlatformId DWORD
* szCSDVersion BYTE 128 dup (?)
* OSVERSIONINFOA ENDS
*
* OSVERSIONINFO equ
*********************************************/
//.net中声明为类
[ StructLayout( LayoutKind.Sequential )]
public class OSVersionInfo
{
public int OSVersionInfoSize;
public int majorVersion;
public int minorVersion;
public int buildNumber;
public int platformId;
[ MarshalAs( UnmanagedType.ByValTStr,SizeConst=128 )]
public String versionString;
}
//或者
//.net中声明为结构
[ StructLayout( LayoutKind.Sequential )]
public struct OSVersionInfo2
{
public int OSVersionInfoSize;
public int majorVersion;
public int minorVersion;
public int buildNumber;
public int platformId;
[ MarshalAs( UnmanagedType.ByValTStr,SizeConst=128 )]
public String versionString;
}
此例中用到MarshalAs特性,它用于描述字段、方法或参数的封送处理格式。用它作为参数前缀并指定目标需要的
数据类型。例如,以下代码将两个参数作为数据类型长指针封送给
Windows API 函数的字符串 (LPStr):
[MarshalAs(UnmanagedType.LPStr)]
String existingfile;
[MarshalAs(UnmanagedType.LPStr)]
String newfile;
注意结构作为参数时候,一般前面要加上ref修饰符,否则会出现错误:对象的引用没有指定对象的实例。
public static extern bool GetVersionEx2( ref
OSVersionInfo2 osvi );
如果在调用平台 invoke
后的任何位置都未引用托管对象,则垃圾回收器可能将完成该托管对象。这将释放资源并使句柄无效,从而导致平台invoke
调用失败。用 HandleRef 包装句柄可
保证在平台 invoke 调用完成前,不对托管对象进行垃圾回收。
例如下面:
);
StringBuilder buffer = new StringBuilder( 5 );
int read = 0;
ReadFile(fs.Handle,buffer,5,out read,0 ); //调用Win
API中的ReadFile函数
由于fs是托管对象,所以有可能在
平台调用还未完成时候被垃圾回收站回收。将
文件流的句柄用HandleRef包装后,就能避免被
垃圾站回收:
public static extern bool ReadFile(
HandleRef hndRef,
StringBuilder buffer,
int numberOfBytesToRead,
out int numberOfBytesRead,
ref Overlapped flag );
......
......
FileMode.Open );
HandleRef hr = new HandleRef( fs,fs.Handle );
StringBuilder buffer = new StringBuilder( 5 );
int read = 0;
// platform invoke will hold reference to HandleRef
until call ends
ReadFile( hr,buffer,5,out read,0 );
分类
Windows API所提供的功能可以归为七类:
1.基础服务(Base Services),提供对Windows系统可用的基础资源的访问接口。比如像:
文件系统(file system)、
外部设备(device)、进程(process)、
线程(thread)以及访问
注册表(Windows registry)和
错误处理机制(error handling)。这些功能接口位于 16位Windows下的kernel.exe、krnl286.exe或krnl386.exe
系统文档中,以及32位Windows下的
kernel32.dll和
advapi32.dll中。
2.
图形设备接口(
GDI),提供功能为:输出图形内容到显示器、打印机以及其他外部
输出设备。它位于16位Windows下的gdi.exe;以及32位Windows下的
gdi32.dll。
3.图形化用户界面(
GUI),提供的功能有创建和管理屏幕和大多数基本控件(control),比如按钮和
滚动条。接收鼠标和
键盘输入,以及其他与GUI有关的功能。这些调用接口位于:16位Windows下的user.exe,以及32位Windows下的
user32.dll。从
Windows XP版本之后,基本控件和通用对话框控件(Common Control Library)的调用接口放在
comctl32.dll中。
4.通用对话框链接库(Common Dialog Box Library),为应用程序提供标准对话框,比如打开/
保存文档对话框、颜色对话框和字体对话框等等。这个链接库位于:16位Windows下的commdlg
.dll中,以及32位Windows下
comdlg32.dll中。它被归类为User Interface API之下。
5.通用控件链接库(Common Control Library),为应用程序提供接口来访问操作系统提供的一些高级控件。比如像:
状态栏(status bar)、
进度条(progress bars)、
工具栏(toolbar)和标签(tab)。这个链接库位于:16位Windows下的
commctrl.dll中,以及32位Windows下comctl32.dll中。。它被归类为User Interface API之下。
6.Windows外壳(Windows Shell),作为Windows API的组成部分,不仅允许应用程序访问Windows外壳提供的功能,还对之有所改进和增强。它位于16位Windows下的
shell.dll中,以及32位Windows下的
shell32.dll中(
Windows 95则在
shlwapi.dll中)。 它被归类为User Interface API之下。
7.
网络服务(Network Services),为访问操作系统提供的多种网络 功能提供接口。它包括
NetBIOS、
Winsock、NetDDE及
RPC等。